/* System includes */
#include <string.h>
#include <assert.h>
#include <stdlib.h>

/* OpenACN includes */
#include "opt.h"
#include "acnstdtypes.h"
#include "acn_port.h"
#include "acn_arch.h"
#include "acn_sdt.h"

#include "dmp.h"
#include "dmpmem.h"
#include "acn_dmp.h"
#include "rlp.h"
#include "sdt.h"
#include "acnlog.h"
#include "marshal.h"


#include "app_dmp.h"


#define LOG_FSTART() acnlog(LOG_DEBUG | LOG_DMP, "%s [", __func__)
#define LOG_FEND() acnlog(LOG_DEBUG | LOG_DMP, "%s ]", __func__)

/* TBD this should probably be changed to do a malloc() for multiple thread use */
static	 uint8_t data[300];
static	 uint8_t property_ptr[300];

void dmp_tx_get_prop(component_t *local_component,
			component_t *foreign_component, dmp_address_t *address)
{
	  uint8_t      *datap;     /* temp pointer used for PDU */
	  int           data_len;  /* length of this PDU */
	  bool          is_reliable = true;

	  /* get pointer into the PDU where the vector byte will be placed */
	  /* we will come back later to set the first two bytes (flag and length) */
	  datap = data+2;

	  /* put the message type (vector) into the pdu */
	  datap = marshalU8(datap, DMP_GET_PROPERTY);

	  /* encode address */
	  datap = dmp_encode_address_header(address, datap, datap+1);

	  /* find the length of this PDU */
	  data_len = datap - data;

	  /* go back and put the flags and length fields into the first two bytes */
	  marshalU16(data, VECTOR_FLAG | HEADER_FLAG | DATA_FLAG | data_len);

	  /* send this DMP PDU. The data will have to be copied by dmp_tx_pdu. */
	  dmp_tx_pdu(local_component, foreign_component, is_reliable, data, data_len);

}


void app_rx_get_property(component_t *local_component, component_t *foreign_component,
			uint8_t address_type, uint8_t *data, uint32_t data_len)
{
	LOG_FSTART();
	uint8_t *datap = NULL;     /* temp pointer used for PDU */
	uint8_t *prop_ptr = property_ptr;
	dmp_address_t *addr = NULL;
	dmp_property_map_t *property;
	int i = 0 ; //for loop
	int return_code = DMP_REASON_SUCCESS;

	addr = malloc(sizeof(dmp_address_t));
	datap = data;
	datap +=  dmp_decode_address_header(address_type,datap,addr);
	acnlog(LOG_DEBUG | LOG_DMP,"DMP_GET_PROPERTY , address %X" ,addr->address_start);

	switch(addr->address_type){
	case SINGLE_ADDRESS_SINGLE_DATA:
		property = property_findby_address(addr->address_start);
		if(property == NULL){
			acnlog(LOG_DEBUG | LOG_DMP,"get property fail");
			return_code = DMP_NOT_A_PROPERTY;
			break;
		}
		prop_ptr = encode_property(property,prop_ptr);
		break;
	case RANGE_ADDRESS_SINGLE_DATA:
	case RANGE_ADDRESS_EQUAL_SIZE_DATA:
	case RANGE_ADDRESS_MIXED_SIZE_DATA: /*get property only divided by address type*/
		for(i=0;i<addr->num_props;i++){
			property = property_findby_address(addr->address_start+(i*addr->address_inc));
			if(property == NULL){
				acnlog(LOG_DEBUG | LOG_DMP,"get property fail");
				return_code = DMP_NOT_A_PROPERTY;
				break;
			}
			prop_ptr = encode_property(property,prop_ptr);
		}
		break;
	}

	if(return_code)
		dmp_tx_get_prop_fail(local_component, foreign_component,addr,return_code);

	dmp_tx_get_prop_reply(local_component, foreign_component,addr,property_ptr,(prop_ptr-property_ptr));
	//release mem
	free(addr);
	LOG_FEND();
}

void app_tx_set_property(component_t *local_component, component_t *foreign_component,
			dmp_address_t *address , uint8_t *ptrProperty, uint16_t sizeofProperty)
{
	LOG_FSTART();
	uint8_t      *datap;     /* temp pointer used for PDU */
	int           data_len;  /* length of this PDU */
	bool          is_reliable = true;
	int 		x = 0; //for loop

	/* get pointer into the PDU where the vector byte will be placed */
	/* we will come back later to set the first two bytes (flag and length) */
	datap = data+2;

	/* put the message type (vector) into the pdu */
	datap = marshalU8(datap, DMP_SET_PROPERTY);

	/* encode address */
	datap = dmp_encode_address_header(address, datap, datap+1);


	for (x=0; x<sizeofProperty; x++) {
	    *datap++ = *ptrProperty++;
	}

	/* find the length of this PDU */
	data_len = datap - data;

	/* go back and put the flags and length fields into the first two bytes */
	marshalU16(data, VECTOR_FLAG | HEADER_FLAG | DATA_FLAG | data_len);

	/* send this DMP PDU. The data will have to be copied by dmp_tx_pdu. */
	dmp_tx_pdu(local_component, foreign_component, is_reliable, data, data_len);

	LOG_FEND();
}

void app_rx_set_property(component_t *local_component, component_t *foreign_component,
		uint8_t address_type, uint8_t *data, uint32_t data_len)
{
	LOG_FSTART();
	uint8_t *datap;     /* temp pointer used for PDU */
	dmp_address_t *addr ;
	dmp_property_map_t *property = NULL;
	int return_code = DMP_REASON_SUCCESS;
	int i = 0;  //for loop

	//we must allocat the memory
	addr = malloc(sizeof(dmp_address_t));
	datap = data;
	datap += dmp_decode_address_header(address_type,datap,addr);
	acnlog(LOG_DEBUG | LOG_DMP,"DMP_GET_PROPERTY , address %X" ,addr->address_start);

/* if address type is non-zero then a range address was specified */
	switch(addr->address_type){
	case SINGLE_ADDRESS_SINGLE_DATA:
		datap += decode_property(addr->address_start,datap,&property);
		if(set_property(property->property.address,property->value)==FAIL){
			acnlog(LOG_DEBUG | LOG_DMP,"set property fail");
			return_code = DMP_NOT_A_PROPERTY;
		}
		break;
	case RANGE_ADDRESS_SINGLE_DATA:
		datap += decode_property(addr->address_start,datap,&property);
		for(i=0;i<addr->num_props;i++){
			if(set_property(property->property.address,property->value)==FAIL){
				acnlog(LOG_DEBUG | LOG_DMP,"set property fail");
				return_code = DMP_NOT_A_PROPERTY;
			}
		}
		break;
	case RANGE_ADDRESS_EQUAL_SIZE_DATA:
	case RANGE_ADDRESS_MIXED_SIZE_DATA: /*get property only divided by address type*/
		for(i=0;i<addr->num_props;i++){
			datap += decode_property(addr->address_start+addr->address_inc*i,datap,&property);
			if(set_property(property->property.address,property->value)==FAIL){
				acnlog(LOG_DEBUG | LOG_DMP,"set property fail");
				return_code = DMP_NOT_A_PROPERTY;
			}
		}
		break;
	}
	//if fail to set return reason
	if(return_code)
		dmp_tx_set_prop_fail(local_component, foreign_component,addr,return_code);

	//release mem
	free(addr);
	LOG_FEND();

}


///test functions
void dmp_get(component_t *local_component, component_t *foreign_component)
{
	acnlog(LOG_DEBUG | LOG_DMP,"DMP tx get property");
	dmp_address_t *addr ;
	addr = malloc(sizeof(dmp_address_t));
	addr->address_type = SINGLE_ADDRESS_SINGLE_DATA;
	addr->address_start = 0x55;
	addr->address_size = ONE_OCTET_ADDRESS;
	addr->is_single_value = true;
	addr->is_virtual = false;
	dmp_tx_get_prop(local_component,foreign_component,addr);
	free(addr);
}

void dmp_set(component_t *local_component, component_t *foreign_component)
{
	acnlog(LOG_DEBUG | LOG_DMP,"DMP tx get property");
	dmp_address_t *addr ;
	uint8_t* propertyStr ;
	uint8_t* property;

	addr = malloc(sizeof(dmp_address_t));
	addr->address_type = SINGLE_ADDRESS_SINGLE_DATA;
	addr->address_start = 0x55;
	addr->address_size = ONE_OCTET_ADDRESS;
	addr->is_single_value = true;
	addr->is_virtual = false;

	if(!(propertyStr = dmpm_createProperty(2))){
		acnlog(LOG_DEBUG | LOG_DMP,"DMP create propertyStr failed");
		return;
	}else{
		property = propertyStr;
		propertyStr =  marshalU16(propertyStr, 2);
		*propertyStr = 0x30;
		*(propertyStr+1) = 0x10;
	}
	app_tx_set_property(local_component,foreign_component,addr,property,2+2);
	free(addr);
}
